package com.gao.b_channel.c;

import org.junit.Test;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Paths;
import java.util.Arrays;

/**
 * @author gao
 * @time 2020/06/19 15:42:04
 * 该例子不重要，可以跳过，完全是出于好奇
 */
public class App2 {

    // Java中，默认使用的码表是unicode，以下例子证明了这一点
    // utf-8在线编码转换：http://www.jsons.cn/unicode/
    // gbk在线编码转换：http://www.dwenzhao.cn/cal/php/hexhanzi.php
    @Test
    public void test() throws IOException {

        // 强制转换‘北’字符为int类型，查看'北'所对应的数字：21271
        char c = '北';
        System.out.println("强制类型转换：" + (int) c);

        // 使用unicode码表对‘北’进行编码：21271，如此就证明了java默认使用的码表是unicode
        String s = "北";
        byte[] bb = s.getBytes("unicode");
        /*
            [-2, -1, 83, 23]
            83 ==> 64 + 16 + 2 + 1 ==> 01010011
            23 ==> 16 + 4 + 2 + 1 ==>  00010111

            01010011 00010111 ==> 01010011 00010111 ==> 16384 + 4096 + 512 + 256 +16 + 4 + 2 + 1 = ‭21271‬
         */
        System.out.println(Arrays.toString(bb));

        // 再看看'北'再gbk码表中的编码是多少：45489  或用16进制表示：b1b1
        // 这也就证明了在不同额码表中，‘北’字符对应的数字是不一样额
        byte[] bb2 = s.getBytes("gbk");
        /*
            [-79, -79]
            -79的源码：11001111
            -79的反码：10110000
            -79的补码：10110001

            10110001 10110001 ==> 1011000110110001 ==> \0xb1b1 ==> 45489
         */
        System.out.println(Arrays.toString(bb2));
    }
    // 现在，我们已经知道，‘北’在unicode中对应的数字为21271，'北'在gbk中对应的数字为45489。记住这个结论，我们继续向下走。


    // 准备一个文件，比如c.txt，在其中写入字符'北'，注意，要保证c.txt文件的编码方式为gbk
    // 按我们的一般思维来分析：我们使用Reader来读取这个c.txt中唯一个‘北’这个字符，由于c.txt文件的编码方式为gbk
    // 所以Reader的read方法应该返回的是'北'在gbk中的数字：45489，实施情况是这样吗? 请看下面的例子：
    @Test
    public void test2() throws IOException {
        // c.txt的编码为gbk，且其中只存放了一个字符：‘北’
        Reader in = new FileReader("file\\c.txt");
        // Reader的read()方法，一次读取一个字节。 问题是，gbk码表中，数字，字母是占1个字节的，而汉字是占两个字节的
        // 这里Reader的read()方法，是如何知道将要读取的字符到底是几个字节呢？ 这里我就不演示了，有兴趣的自己找资料吧。
        int n = in.read();
        // 打印结果：       北           21271
        System.out.println(n + " " + (char) n);
        in.close();

        /*
         我们发现，从编码为gbk的c.txt文件中，读取出来的'北'字符，对应的数字并不是gbk中的45489，
         而对应的数字是21271，这个数字是'北'在unicode码表中所对应的数字。这是为什么呢？？

         实际的情况是这样的（重点，重点，重点！！！）：
         Reader的read()从c.txt(45489)中读取一个字符 ==> gbk解码，找到了'北' ==> '北'再在unicode中进行编码，找到21271 ==> 返回到read的调用处

         Reader的read方法为什么要这么搞呢？ 字符流会使用到码表，同时我个人认为是为了符合“java默认的码表为unicode”。
        */
    }

    // 如果我们使用的是字节码，而非字符流时，就能看到c.txt中存放的确实是‘北’在gbk码表中对应的数字：45489
    @Test
    public void test3() throws IOException {
        InputStream in = new FileInputStream("file\\c.txt");
        // 因为gbk中，汉字占2个字节，所以这里需要读取两次。
        int a = in.read();
        int b = in.read();
        System.out.println(a);  // 177
        System.out.println(b);  // 177

        /*
            见证奇迹的时刻：
            177的源码： 128 + 32 + 16 + 1 ==> 10110001
            10110001 10110001 ==> 1011000110110001 ==> ‭45,489‬
         */
        in.close();
    }

    // 让我们尝试使用nio来读取c.txt中的‘北’吧！
    @Test
    public void test4() throws IOException {
        FileChannel srcChannel = FileChannel.open(Paths.get("file\\c.txt"));
        ByteBuffer buf = ByteBuffer.allocate(10);
        srcChannel.read(buf);
        buf.flip();

        byte[] array = buf.array();
        /*
         以下代码输出结果是：[-79, -79, 0, 0, 0, 0, 0, 0, 0, 0]
         -79的源码：11001111
         -79的反码：10110000
         -79的补码：10110001
         10110001 10110001 ==> 1011000110110001 ==> \0xb1b1 ==> 45489
         */
        System.out.println(Arrays.toString(array));
    }
}
